package com.rpframework.core.mybatis.mapper.plugin;

import com.rpframework.core.mybatis.MapperUtils;
import com.rpframework.core.mybatis.mapper.plugin.template.Mapper;
import com.rpframework.core.utils.FreemarkerUtil;
import com.rpframework.utils.ClassUtils;
import com.rpframework.utils.FileUtils;
import com.rpframework.utils.PackageUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.builder.BaseBuilder;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.type.TypeAliasRegistry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * mybatis 汪mapper添加基本的ResultMap 和两个别名字段的ResultMap
 * Created by jump on 2016/7/6.
 */
@Component
public class MapperScannerConfigurer extends BaseBuilder {

    private static final Log log = LogFactory.getLog(MapperScannerConfigurer.class);

    @Autowired
    private org.mybatis.spring.mapper.MapperScannerConfigurer mapperScannerConfigurer;
    private Configuration configuration;
    @Autowired
    SqlSessionFactory sqlSessionFactory;

    private static TypeAliasRegistry typeAliasRegistry;

    private Set<Class<?>> basePackageClass;
    /**
     * 别名包
     */
    private String typeAliasesPackage;


    private String getTypeAliasesPackage() {
        return typeAliasesPackage;
    }

    private void setTypeAliasesPackage(String typeAliasesPackage) {
        this.typeAliasesPackage = typeAliasesPackage;
    }

    /**
     * 扫描包
     */
    private String basePackage;

    /**
     * 子文件夹名字
     */
    private static final String baseDirSonName = "mybatis.mapper";
    /**
     * 存放零食文件基础路径
     */
    private static final String baseDir = System.getProperty("user.dir") + File.separator + baseDirSonName;

    MapperScannerConfigurer(@Qualifier SqlSessionFactory sqlSession, org.mybatis.spring.mapper.MapperScannerConfigurer
            mapperScannerConfigurer) {
        this(sqlSession, mapperScannerConfigurer, null);
    }


    @Autowired
    MapperScannerConfigurer(@Qualifier SqlSessionFactory sqlSession, org.mybatis.spring.mapper.MapperScannerConfigurer
            mapperScannerConfigurer, String typeAliasesPackage) {
        this(sqlSession, mapperScannerConfigurer, null, typeAliasesPackage);
    }

    MapperScannerConfigurer(@Qualifier SqlSessionFactory sqlSession, String basePackage, String typeAliasesPackage) {
        this(sqlSession, null, basePackage, typeAliasesPackage);
    }

    MapperScannerConfigurer(@Qualifier Configuration configuration, String basePackage, String typeAliasesPackage) {
        super(configuration);
        setTypeAliasesPackage(typeAliasesPackage);
        this.configuration = configuration;
        this.basePackage = basePackage;

        typeAliasRegistry = super.typeAliasRegistry;

        //添加进mybatis
        File baseDirFile = new File(getBaseDir());
        if (!baseDirFile.exists()) {
            boolean mkdirs = baseDirFile.mkdirs();
            if (!mkdirs) {
                throw new RuntimeException("文件创建失败");
            }
        } else {
            FileUtils.deleteDir(baseDirFile);
            boolean mkdirs = baseDirFile.mkdirs();
            if (!mkdirs) {
                throw new RuntimeException("文件创建失败");
            }
        }


        //生成文件
        Map<String, Class<?>> typeAliases = typeAliasRegistry.getTypeAliases();
        for (Map.Entry<String, Class<?>> type :
                typeAliases.entrySet()) {

            Class<?> aClass = type.getValue();
            if (checkIsRegEntity(aClass)) {
                Mapper mapper = new Mapper();
                mapper.setNamespace(getNamespace(aClass));
                //map
                mapper.setResultMaps(new HashSet<>(MapperUtils.parseResultMaps(aClass)));

                File file = FreemarkerUtil.getFile("mybatis/mapper/TemplateMapper.xml.ftl", mapper,
                        new File(baseDirFile.getPath() + File.separator + aClass.getName() + ".xml"));

                Resource mapperLocation = new FileSystemResource(file);

                XMLMapperBuilder xmlMapperBuilder;
                try {
                    xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
                            configuration, mapperLocation.toString(), configuration.getSqlFragments());
                    xmlMapperBuilder.parse();
                } catch (Exception e) {
                    log.error("读取Mapper.xml文件失败", e);
                }

            }
        }
    }

    private MapperScannerConfigurer(@Qualifier SqlSessionFactory sqlSession, org.mybatis.spring.mapper.MapperScannerConfigurer
            mapperScannerConfigurer, String basePackage, String typeAliasesPackage) {
        super(sqlSession.getConfiguration());
        this.typeAliasesPackage = typeAliasesPackage;
        this.sqlSessionFactory = sqlSession;
        this.configuration = sqlSessionFactory.getConfiguration();
        this.mapperScannerConfigurer = mapperScannerConfigurer;
        this.basePackage = basePackage;

        typeAliasRegistry = super.typeAliasRegistry;

        //添加进mybatis
        File baseDirFile = new File(baseDir);
        if (!baseDirFile.exists()) {
            boolean mkdirs = baseDirFile.mkdirs();
            if (!mkdirs) {
                throw new RuntimeException("文件创建失败");
            }
        } else {
            FileUtils.deleteDir(baseDirFile);
            boolean mkdirs = baseDirFile.mkdirs();
            if (!mkdirs) {
                throw new RuntimeException("文件创建失败");
            }
        }


        //生成文件
        Map<String, Class<?>> typeAliases = typeAliasRegistry.getTypeAliases();
        for (Map.Entry<String, Class<?>> type :
                typeAliases.entrySet()) {

            Class<?> aClass = type.getValue();
            if (checkIsRegEntity(aClass)) {
                Mapper mapper = new Mapper();
                mapper.setNamespace(getNamespace(aClass));
                //map
                mapper.setResultMaps(new HashSet<>(MapperUtils.parseResultMaps(aClass)));

                File file = FreemarkerUtil.getFile("mybatis/mapper/TemplateMapper.xml.ftl", mapper,
                        new File(baseDirFile.getPath() + File.separator + aClass.getName() + ".xml"));

                Resource mapperLocation = new FileSystemResource(file);

                XMLMapperBuilder xmlMapperBuilder;
                try {
                    xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
                            configuration, mapperLocation.toString(), configuration.getSqlFragments());
                } catch (IOException e) {
                    log.error("读取Mapper.xml文件失败", e);
                    continue;
                }
                xmlMapperBuilder.parse();
            }
        }
    }

    /**
     * 获取命名空间
     *
     * @return 命名空间
     */
    private String getNamespace(Class<?> entity) {
        //初始化扫描包
        if (basePackage == null) {
            if (mapperScannerConfigurer != null) {
                basePackage = ClassUtils.getFieldValue(mapperScannerConfigurer,
                        "basePackage", String.class);
            }
            if (StringUtils.isBlank(basePackage)) {
                throw new RuntimeException("basePackage不能为空");
            }
        }

        //初始化扫描包下的类
        if (basePackageClass == null) {
            basePackageClass = PackageUtils.scanClasses(basePackage);
        }

        for (Class<?> one :
                basePackageClass) {
            Type[] genericInterfaces = one.getGenericInterfaces();
            try {
                assert genericInterfaces != null;
                for (Type genericInterface :
                        genericInterfaces) {
                    Type[] params = ((ParameterizedType) genericInterface).getActualTypeArguments();
                    assert params != null;
                    for (Type param :
                            params) {
                        if (param.getTypeName().equals(entity.getName())) {
                            return one.getName();
                        }
                    }
                }
            } catch (Exception ignored) {
                log.warn(ignored.getMessage());
            }
        }
        return entity.getName();
    }

    /**
     * 检测此类是否需要注册
     *
     * @param clazz 字节码
     * @return 是返回true
     */

    private boolean checkIsRegEntity(Class<?> clazz) {

        if (StringUtils.isBlank(getTypeAliasesPackage())) {
            return false;
        }

        String[] typeAliasesPackages = getTypeAliasesPackage().split(",");
        for (String typeAliasesPackageOne :
                typeAliasesPackages) {
            try {
                if (clazz.getPackage().getName().equals(typeAliasesPackageOne)) {
                    return true;
                }
            } catch (Exception e) {
                return false;
            }

        }
        return false;
    }

    /**
     * 别名换取类名
     *
     * @param classAlias 实体类
     * @return 字节码
     */
    public static Class<?> aliasChangeClass(String classAlias) {
        return typeAliasRegistry.resolveAlias(classAlias);
    }

    /**
     * 获取mapper存放目录
     *
     * @return 绝对目录
     */
    private String getBaseDir() {
        return baseDir + File.separator + basePackage;
    }

}
